home *** CD-ROM | disk | FTP | other *** search
- /* tfm_input.c: read a TFM file.
-
- Copyright (C) 1992 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "config.h"
-
- #include "file-input.h"
- #include "tfm.h"
-
-
- /* If true, print out what we read. */
- static boolean tracing_tfm_input = false;
-
- /* These identify the file we're reading. */
- static FILE *tfm_input_file;
- static string tfm_input_name;
-
-
- /* A copy of the character information. We rely on the `exists' member
- of all these characters being initialized to zero (i.e., `false') by
- virtue of the variable being static. */
- static tfm_char_type tfm_char_table[TFM_SIZE];
-
- /* A copy of the global information. */
- static tfm_global_info_type *global_info = NULL;
-
- /* Global information which is only useful to us, not the user. The
- `..._pos' members record the location of the various components of
- the file. The member `param_word_count' says how many words of
- header information are present in this TFM file. */
- typedef struct
- {
- byte_count_type char_info_pos;
- byte_count_type width_pos;
- byte_count_type height_pos;
- byte_count_type depth_pos;
- byte_count_type italic_correction_pos;
- byte_count_type lig_kern_pos;
- byte_count_type kern_pos;
- unsigned param_word_count;
- } tfm_header_type;
-
- static tfm_header_type tfm_header;
-
-
- /* Low-level input. These macros call the corresponding routines in
- kbase, using the static variables for the input file and filename. */
- #define TFM_FTELL() xftell (tfm_input_file, tfm_input_name)
- #define TFM_FSEEK(offset, from_where) \
- xfseek (tfm_input_file, offset, from_where, tfm_input_name)
- #define TFM_GET_BYTE() get_byte (tfm_input_file, tfm_input_name)
- #define TFM_GET_TWO() get_two (tfm_input_file, tfm_input_name)
- #define TFM_GET_FOUR() get_four (tfm_input_file, tfm_input_name)
-
-
- static tfm_char_type get_char ();
- static string tfm_get_bcpl_string (void);
- static real tfm_get_fix_word (void);
- static void get_lig_kern_program (list_type *, list_type *);
- static real tfm_get_scaled_fix (void);
- static void get_tfm_header (void);
- static void get_tfm_params (void);
-
- /* Routines to start and finish reading a file. (For the user to call.) */
-
- boolean
- tfm_open_input_file (string filename)
- {
- if (tfm_input_file != NULL)
- FATAL2 ("tfm_open_input_file: Attempt to open `%s', but `%s' is
- already open", filename, tfm_input_name);
-
- tfm_input_name = filename;
- tfm_input_file = fopen (filename, "r");
-
- return tfm_input_file != NULL;
- }
-
-
- void
- tfm_close_input_file ()
- {
- assert (tfm_input_file != NULL);
-
- xfclose (tfm_input_file, tfm_input_name);
-
- tfm_input_file = NULL;
- tfm_input_name = NULL;
- global_info = NULL;
- }
-
-
- /* This is just avoids all callers having to keep a global around with
- the TFM name they used. */
-
- string
- tfm_input_filename ()
- {
- if (tfm_input_file == NULL)
- return NULL;
-
- return tfm_input_name;
- }
-
- /* Some of the global information comes from the beginning of the file,
- and some from the end. If we've already read the information from
- the file, we don't do it again. */
-
- tfm_global_info_type
- tfm_get_global_info ()
- {
- assert (tfm_input_file != NULL);
-
- /* Only read the file once. */
- if (global_info != NULL) return *global_info;
-
- global_info = XTALLOC1 (tfm_global_info_type);
- get_tfm_header ();
- get_tfm_params ();
-
- return *global_info;
- }
-
-
- /* Now comes convenience routines to get particular parts of the global
- info. */
-
- unsigned
- tfm_get_checksum ()
- {
- (void) tfm_get_global_info ();
- return TFM_CHECKSUM (*global_info);
- }
-
-
- double
- tfm_get_design_size ()
- {
- (void) tfm_get_global_info ();
- return TFM_DESIGN_SIZE (*global_info);
- }
-
-
- string
- tfm_get_coding_scheme ()
- {
- (void) tfm_get_global_info ();
- return TFM_CODING_SCHEME (*global_info);
- }
-
-
- double
- tfm_get_interword_space ()
- {
- (void) tfm_get_global_info ();
- return TFM_FONTDIMEN (*global_info, TFM_SPACE_PARAMETER);
- }
-
-
- double
- tfm_get_x_height ()
- {
- (void) tfm_get_global_info ();
- return TFM_FONTDIMEN (*global_info, TFM_XHEIGHT_PARAMETER);
- }
-
-
- /* Here we read the information at the beginning of the file. We store
- the result into the static variables `global_info' and
- `tfm_header'. */
-
- static void
- get_tfm_header ()
- {
- two_bytes file_length, header_length;
- two_bytes width_word_count, height_word_count, depth_word_count;
- two_bytes italic_correction_word_count, lig_kern_word_count;
- two_bytes kern_word_count, extensible_word_count;
-
- /* The header is at the beginning of the file, naturally. */
- TFM_FSEEK (0, SEEK_SET);
-
- file_length = TFM_GET_TWO ();
- header_length = TFM_GET_TWO ();
-
- global_info->first_charcode = TFM_GET_TWO ();
- global_info->last_charcode = TFM_GET_TWO ();
- width_word_count = TFM_GET_TWO ();
- height_word_count = TFM_GET_TWO ();
- depth_word_count = TFM_GET_TWO ();
- italic_correction_word_count = TFM_GET_TWO ();
- lig_kern_word_count = TFM_GET_TWO ();
- kern_word_count = TFM_GET_TWO ();
- extensible_word_count = TFM_GET_TWO ();
- tfm_header.param_word_count = TFM_GET_TWO ();
- TFM_FONTDIMEN_COUNT (*global_info) = tfm_header.param_word_count;
-
- tfm_header.char_info_pos = (6 + header_length) * 4;
- tfm_header.width_pos = tfm_header.char_info_pos
- + (global_info->last_charcode
- - global_info->first_charcode + 1) * 4;
- tfm_header.height_pos = tfm_header.width_pos + width_word_count * 4;
- tfm_header.depth_pos = tfm_header.height_pos + height_word_count * 4;
- tfm_header.italic_correction_pos = tfm_header.depth_pos
- + depth_word_count * 4;
- tfm_header.lig_kern_pos = tfm_header.italic_correction_pos
- + italic_correction_word_count * 4;
- tfm_header.kern_pos = tfm_header.lig_kern_pos + lig_kern_word_count * 4;
- /* We don't care about the extensible table. */
-
- if (header_length < 2)
- FATAL2 ("TFM header of `%s' has only %u word(s)", tfm_input_name,
- header_length);
-
- TFM_CHECKSUM (*global_info) = TFM_GET_FOUR ();
- TFM_DESIGN_SIZE (*global_info) = tfm_get_fix_word ();
-
- /* Although the coding scheme might be interesting to the caller, the
- font family and face byte probably aren't. So we don't read them. */
- TFM_CODING_SCHEME (*global_info)
- = header_length > 2 ? tfm_get_bcpl_string () : "unspecified";
-
- if (tracing_tfm_input)
- printf ("TFM checksum = %u, design_size = %fpt, coding scheme = `%s'.\n",
- TFM_CHECKSUM (*global_info),
- TFM_DESIGN_SIZE (*global_info),
- TFM_CODING_SCHEME (*global_info));
- }
-
-
- /* Although TFM files are only usable by TeX if they have at least seven
- parameters, that is not a requirement of the file format itself, so
- we don't impose it. And they can have many more than seven, of
- course. We do impose a limit of TFM_MAX_FONT_PARAMETERS. We assume
- that `tfm_header' has already been filled in. */
-
- static void
- get_tfm_params ()
- {
- unsigned this_param;
-
- /* If we have no font parameters at all, we're done. */
- if (tfm_header.param_word_count == 0)
- return;
-
- /* Move to the beginning of the parameter table in the file. */
- TFM_FSEEK (-4 * tfm_header.param_word_count, SEEK_END);
-
- /* It's unlikely but possible that this TFM file has more fontdimens
- than we can deal with. */
- if (tfm_header.param_word_count > TFM_MAX_FONTDIMENS)
- {
- WARNING3 ("%s: TFM file has %u parameters, which is more than the
- %u I can handle",
- tfm_input_name, tfm_header.param_word_count,
- TFM_MAX_FONTDIMENS);
- tfm_header.param_word_count = TFM_MAX_FONTDIMENS;
- }
-
- /* The first parameter is different than all the rest, because it
- isn't scaled by the design size. */
- TFM_FONTDIMEN (*global_info, TFM_SLANT_PARAMETER) = tfm_get_fix_word ();
-
- for (this_param = 2; this_param <= tfm_header.param_word_count;
- this_param++)
- TFM_FONTDIMEN (*global_info, this_param) = tfm_get_scaled_fix ();
-
- if (tracing_tfm_input)
- {
- for (this_param = 1; this_param <= tfm_header.param_word_count;
- this_param++)
- printf ("TFM parameter %d: %.3f", this_param,
- TFM_FONTDIMEN (*global_info, this_param));
- }
- }
-
- /* Read every character in the TFM file, storing the result in the
- static `tfm_char_table'. We return a copy of that variable. */
-
- tfm_char_type *
- tfm_get_chars ()
- {
- tfm_char_type *tfm_chars;
- unsigned this_char;
-
- assert (tfm_input_file != NULL);
-
- tfm_get_global_info ();
-
- for (this_char = global_info->first_charcode;
- this_char <= global_info->last_charcode;
- this_char++)
- /* This fills in the `tfm_char_table' global. */
- (void) tfm_get_char (this_char);
-
- /* Return a copy, so our information can't get corrupted. */
- tfm_chars = XTALLOC (TFM_SIZE, tfm_char_type);
- memcpy (tfm_chars, tfm_char_table, sizeof (tfm_char_table));
- return tfm_chars;
- }
-
-
- /* Read the character CODE. If the character doesn't exist, return
- NULL. If it does, save the information in `tfm_char_table', as well
- as returning it. */
-
- tfm_char_type *
- tfm_get_char (charcode_type code)
- {
- assert (tfm_input_file != NULL);
-
- tfm_get_global_info ();
-
- /* If the character is outside the declared bounds in the file, don't
- try to read it. Just return NULL. */
- if (code < global_info->first_charcode
- || code > global_info->last_charcode)
- return NULL;
-
- /* Move to the appropriate place in the `char_info' array. */
- TFM_FSEEK (tfm_header.char_info_pos
- + (code - global_info->first_charcode) * 4,
- SEEK_SET);
-
- /* Read the character. */
- tfm_char_table[code] = get_char ();
-
- /* If it exists, return a pointer to it. We return a copy, so our
- information can't get corrupted. */
- if (!TFM_CHAR_EXISTS (tfm_char_table[code]))
- return NULL;
- else
- {
- tfm_char_type *c = XTALLOC1 (tfm_char_type);
-
- TFM_CHARCODE (tfm_char_table[code]) = code;
- *c = tfm_char_table[code];
- return c;
- }
- }
-
-
- /* We assume we are positioned at the beginning of a `char_info' word.
- We read that word to get the indexes into the dimension tables; then
- we go read the tables to get the values (if the character exists). */
-
- static tfm_char_type
- get_char ()
- {
- one_byte width_index, height_index, depth_index, italic_correction_index;
- one_byte packed;
- one_byte tag, remainder;
- tfm_char_type tfm_char;
-
- /* Read the char_info word. */
- width_index = TFM_GET_BYTE ();
-
- packed = TFM_GET_BYTE ();
- height_index = (packed & 0xf0) >> 4;
- depth_index = packed & 0x0f;
-
- packed = TFM_GET_BYTE ();
- italic_correction_index = (packed & 0xfc) >> 6;
- tag = packed & 0x3;
-
- remainder = TFM_GET_BYTE ();
-
- tfm_char = tfm_new_char ();
-
- #define GET_CHAR_DIMEN(d) \
- if (d##_index != 0) \
- { \
- TFM_FSEEK (tfm_header.##d##_pos + d##_index*4, SEEK_SET); \
- tfm_char.fix_##d = TFM_GET_FOUR (); \
- tfm_char.##d = fix_to_real (tfm_char.fix_##d) \
- * global_info->design_size; \
- }
-
- GET_CHAR_DIMEN (width);
- GET_CHAR_DIMEN (height);
- GET_CHAR_DIMEN (depth);
- GET_CHAR_DIMEN (italic_correction);
-
- /* The other condition for a character existing is that it be between
- the first and last character codes given in the header. We've
- already assumed that's true (or we couldn't be positioned at a
- `char_info_word'). */
- TFM_CHAR_EXISTS (tfm_char) = width_index != 0;
-
- if (tracing_tfm_input)
- {
- printf (" width = %f, height = %f, ", tfm_char.width, tfm_char.height);
- printf ("depth = %f, ic = %f.\n", tfm_char.depth,
- tfm_char.italic_correction);
- }
-
- if (tag == 1)
- {
- TFM_FSEEK (tfm_header.lig_kern_pos + remainder * 4, SEEK_SET);
- get_lig_kern_program (&(tfm_char.ligature), &(tfm_char.kern));
- }
-
- /* We don't handle the other tags. */
- return tfm_char;
- }
-
- /* Read a ligature/kern program at the current position, storing the
- result into *LIGATURE and *KERN. We don't distinguish all the kinds
- of ligatures that Metafont can output. */
-
- #define STOP_FLAG 128
- #define KERN_FLAG 128
-
- static void
- get_lig_kern_program (list_type *ligature, list_type *kern)
- {
- boolean end_of_program;
-
- assert (ligature != NULL && kern != NULL);
-
- do
- {
- one_byte next_char;
- boolean kern_step;
- one_byte remainder;
-
- end_of_program = TFM_GET_BYTE () >= STOP_FLAG;
-
- next_char = TFM_GET_BYTE ();
- kern_step = TFM_GET_BYTE () >= KERN_FLAG;
- remainder = TFM_GET_BYTE ();
-
- if (tracing_tfm_input)
- printf (" if next = %u (%c), ", next_char, next_char);
-
- if (kern_step)
- {
- byte_count_type old_pos = TFM_FTELL ();
- tfm_kern_type *kern_element = LIST_TAPPEND (kern, tfm_kern_type);
-
- kern_element->character = next_char;
-
- TFM_FSEEK (tfm_header.kern_pos + remainder * 4, SEEK_SET);
- kern_element->kern = tfm_get_scaled_fix ();
- TFM_FSEEK (old_pos, SEEK_SET);
-
- if (tracing_tfm_input)
- printf ("kern %f.\n", kern_element->kern);
- }
- else
- {
- tfm_ligature_type *ligature_element
- = LIST_TAPPEND (ligature, tfm_ligature_type);
-
- ligature_element->character = next_char;
- ligature_element->ligature = remainder;
-
- if (tracing_tfm_input)
- printf ("ligature %d (hex %x).\n",
- ligature_element->ligature, ligature_element->ligature);
- }
- } while (!end_of_program);
- }
-
- /* Most quantities are fixed-point fractions. */
-
- static real
- tfm_get_fix_word ()
- {
- return fix_to_real (TFM_GET_FOUR ());
- }
-
-
- /* Dimensions are a `fix_word' scaled by the design size. */
-
- static real
- tfm_get_scaled_fix ()
- {
- return tfm_get_fix_word () * global_info->design_size;
- }
-
-
- static string
- tfm_get_bcpl_string ()
- {
- unsigned string_length = TFM_GET_BYTE ();
- string s = get_n_bytes (string_length, tfm_input_file, tfm_input_name);
- s = xrealloc (s, string_length + 1);
- s[string_length] = 0;
-
- return s;
- }
-